import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'http_request.dart';
import 'error_interceptor.dart';
import 'request_exception.dart';

class RequestManager {
  final Dio _dio = Dio();

  factory RequestManager() => _sharedInstance();

  static RequestManager? _instance;

  RequestManager._() {
    // 具体初始化代码
    //忽略https证书验证,仅对App有效，对web无效
    /*if (_dio.httpClientAdapter.runtimeType is DefaultHttpClientAdapter) {
      (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) {
          return true;
        };
      };
    }*/
    _addDefaultInterceptor();
  }

  static RequestManager _sharedInstance() {
    if (_instance == null) {
      _instance = RequestManager._();
    }
    return _instance!;
  }

  void initInterceptor(List<Interceptor> interceptors,
      {ExceptionTextDelegate? delegate}) {
    _dio.interceptors.clear();
    _dio.interceptors.addAll(interceptors);
    _addDefaultInterceptor(delegate: delegate);
  }

  void _addDefaultInterceptor({ExceptionTextDelegate? delegate}) {
    if (kDebugMode) {
      _dio.interceptors.add(LogInterceptor(responseBody: false)); //开启请求日志
    }
    _dio.interceptors
        .add(ErrorInterceptor(delegate: delegate ?? ExceptionTextDelegate()));
  }

  void addRequest(BaseHttpRequest request) {
    String url = request.baseUrl + request.path;
    Options options = Options(
        sendTimeout: request.timeOut,
        receiveTimeout: request.timeOut,
        responseType: request.responseType,
        followRedirects: false,
        headers: request.headers);

    _dio.options.baseUrl = request.baseUrl;
    _dio.options.connectTimeout = request.timeOut;
    try {
      switch (request.method) {
        case HttpRequestMethod.get:
          _get(url: url, request: request, options: options);
          break;
        case HttpRequestMethod.post:
          _post(url: url, request: request, options: options);
          break;
        case HttpRequestMethod.delete:
          _delete(url: url, request: request, options: options);
          break;
        case HttpRequestMethod.put:
          _put(url: url, request: request, options: options);
          break;
        default:
          debugPrint('request manager not this method ===: ${request.method}');
      }
    } catch (error) {
      if (error is DioError) {
        _errorHandler(request, error);
      } else {
        _errorHandler(
            request,
            DioError(
                requestOptions: RequestOptions(path: request.path),
                error: error));
      }
    }
  }

  void _post({
    required String url,
    required BaseHttpRequest request,
    required Options options,
  }) {
    _dio
        .post(url,
            data: request.parameters,
            cancelToken: request.token,
            onSendProgress: request.progressCallback,
            options: options)
        .then((value) => _responseHandler(request, value))
        .onError((error, stackTrace) {
      if (error is DioError) {
        _errorHandler(request, error);
      } else {
        _errorHandler(
            request,
            DioError(
                requestOptions: RequestOptions(path: request.path),
                error: error));
      }
    });
  }

  void _get({
    required String url,
    required BaseHttpRequest request,
    required Options options,
  }) {
    _dio
        .get(url,
            queryParameters: request.parameters,
            cancelToken: request.token,
            onReceiveProgress: request.progressCallback,
            options: options)
        .then((value) => _responseHandler(request, value))
        .onError((error, stackTrace) {
      if (error is DioError) {
        _errorHandler(request, error);
      } else {
        _errorHandler(
            request,
            DioError(
                requestOptions: RequestOptions(path: request.path),
                error: error));
      }
    });
  }

  void _delete({
    required String url,
    required BaseHttpRequest request,
    required Options options,
  }) {
    _dio
        .delete(url,
            data: request.parameters,
            queryParameters: request.parameters,
            cancelToken: request.token,
            options: options)
        .then((value) => _responseHandler(request, value))
        .onError((error, stackTrace) {
      if (error is DioError) {
        _errorHandler(request, error);
      } else {
        _errorHandler(
            request,
            DioError(
                requestOptions: RequestOptions(path: request.path),
                error: error));
      }
    });
  }

  void _put({
    required String url,
    required BaseHttpRequest request,
    required Options options,
  }) {
    _dio
        .put(url,
            data: request.parameters,
            queryParameters: request.parameters,
            cancelToken: request.token,
            onSendProgress: request.progressCallback,
            options: options)
        .then((value) => _responseHandler(request, value))
        .onError((error, stackTrace) {
      if (error is DioError) {
        _errorHandler(request, error);
      } else {
        _errorHandler(
            request,
            DioError(
                requestOptions: RequestOptions(path: request.path),
                error: error));
      }
    });
  }

  void _errorHandler(BaseHttpRequest request, DioError error) {
    if (!redirectUrl(request, error)) {
      if (CancelToken.isCancel(error)) {
        if (kDebugMode) {
          print('Request canceled! ' + (error.message ?? ''));
        }
        request.status = HttpRequestStatus.canceled;
      } else {
        request.status = HttpRequestStatus.finished;
      }
      if (error.error is RequestException) {
        _handlerErrorCode(request, error, error.error as RequestException);
      } else {
        if (request.status == HttpRequestStatus.canceled) {
          _handlerErrorCode(
              request, error, UnknownException(600, error.message ?? ''));
          return;
        }
        _handlerErrorCode(
            request, error, UnknownException(-1, error.message ?? ''));
      }
    }
  }

  void _handlerErrorCode(
      BaseHttpRequest request, DioError error, RequestException exception) {
    if (request.handleErrorCode.contains(error.response?.statusCode)) {
      _responseHandler(request, error.response!);
    } else {
      request.didFinishFailure(exception);
    }
  }

  void _responseHandler(BaseHttpRequest request, Response<dynamic> value) {
    request.status = HttpRequestStatus.finished;
    request.didFinishSuccess(value);
  }

  bool redirectUrl(BaseHttpRequest request, DioError error) {
    if (error.response?.statusCode == 301 ||
        error.response?.statusCode == 302) {
      Response<dynamic> value =
          Response(requestOptions: RequestOptions(path: ''));
      value.data = error.response?.headers.map['location']!.first;
      _responseHandler(request, value);
      return true;
    }
    return false;
  }
}
